﻿#include "serialportobj.h"

/**
 * @brief SerialPortObj::SerialPortObj  对qt串口的封装类对象,需要创建一个QThread,并且
 *                  把此moveToThread(),连接started信号,再连接读数据信号槽即可读取数据.
 * @param deviceName  设备名,用户可自定义
 * @param portName  串口名,如"COM1"
 * @param portBaud  波特率
 * @param parity    校验位,默认无校验
 * @param parent    父对象的指针
 */
SerialPortObj::SerialPortObj( const QString& deviceName, const QString& portName, int portBaud, const QSerialPort::Parity parity, QObject* parent )
    : QObject( parent ), serial_port_( nullptr ), port_name_( portName ), device_name_( deviceName ), baud_rate_( portBaud ), parity_( parity ), is_opened_( false ), is_connected_( false ) {}

SerialPortObj::~SerialPortObj() {
    qDebug() << "destroy SerialPortObj";
    if ( serial_port_ ) {
        if ( serial_port_->isOpen() ) { serial_port_->close(); }
        delete serial_port_;
        serial_port_ = nullptr;
    }
}

/**
 * @brief SerialPortObj::init 对串口进行初始化
 */
void SerialPortObj::init( bool isImmediateRead, int msec ) {
    serial_port_ = new QSerialPort( port_name_ );

    if ( isImmediateRead )  //无需延时接收
    {
        connect( serial_port_, SIGNAL( readyRead() ), this, SLOT( sltImmediateReadyRead() ) );
    }
    else  //需要延时接收，并且设定延时时间参数
    {
        delayMsec = msec;
        connect( serial_port_, SIGNAL( readyRead() ), this, SLOT( sltHandleReadyRead() ) );
    }
    //设置波特率
    serial_port_->setBaudRate( baud_rate_ );
    //设置校验位
    serial_port_->setParity( parity_ );
    //设置数据位
    serial_port_->setDataBits( QSerialPort::Data8 );
    //设置停止位
    serial_port_->setStopBits( QSerialPort::OneStop );
    //设置无硬件流控制
    serial_port_->setFlowControl( QSerialPort::NoFlowControl );

    if ( serial_port_->open( QIODevice::ReadWrite ) ) {
        qDebug() << this->deviceName() << "串口打开成功!";
        is_opened_ = true;
    }
    else {
        qCritical() << this->deviceName() << " 串口打开失败!错误原因:" << serial_port_->errorString();
        is_opened_ = false;
    }
}

bool SerialPortObj::isOpened() const {
    return is_opened_;
}

bool SerialPortObj::isConnected() const {
    return is_connected_;
}

QSerialPort* SerialPortObj::SerialPort() const {
    return serial_port_;
}

QString SerialPortObj::deviceName() const {
    return device_name_;
}

/**
 * @brief SerialPortObj::sltHandleReadyRead 槽函数，当收到串口信号readyRead后，进行一定的延时在接收数据，以保证收到的
 *          数据不会出现收到半截的情况。
 *          2018.8.27 note:实际上不进行延时也可，只要接收频率>5ms亦不会出现粘包
 *          2018.8.31 note:不进行延时的话，当数据包过长时会出现半包现象，所以应该加上延时
 */
void SerialPortObj::sltHandleReadyRead() {
    //启动定时器，接收18毫秒数据（根据情况设定），防止数据分成几段
    QTimer::singleShot( delayMsec, Qt::PreciseTimer, this, SLOT( sltReadData() ) );
}

void SerialPortObj::sltImmediateReadyRead() {
    sltReadData();
}

/**
 * @brief SerialPortObj::sltReadData 槽函数，延时结束后，读取缓冲区的所有字节，并发出信号
 */
void SerialPortObj::sltReadData() {
    QMutexLocker locker( &mutex_ );  //加锁

    if ( serial_port_->bytesAvailable() <= 0 ) { return; }
    is_connected_   = true;
    QByteArray data = serial_port_->readAll();
    emit       sigReadData( data );
    // serial_port_->clear();
    // qDebug() << serial_port_->portName() << " " << data.toHex() << " recv size:" << data.size();
}

/**
 * @brief SerialPortObj::sltSendData 槽函数，用于向串口写数据，外部需要一个信号与此槽进行connect
 * @param buffData 需要写入的数据
 */
void SerialPortObj::sltSendData( SerialPortObj* obj, const QByteArray& buffData ) {
    QMutexLocker locker( &mutex_ );  //加锁

    if ( !buffData.isEmpty() && ( obj == this ) ) {
        serial_port_->write( buffData );
        // qDebug() << serial_port_->portName() << "send data:" << buffData;
    }
}
